package com.hui.admin.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hui.admin.common.enums.ErrorCodeEnum;
import com.hui.admin.common.exception.AnnouncementException;
import com.hui.admin.common.exception.UserException;
import com.hui.admin.dto.request.AnnouncementDeleteBatchRequestDTO;
import com.hui.admin.dto.request.AnnouncementExportRequestDTO;
import com.hui.admin.dto.request.AnnouncementPageRequestDTO;
import com.hui.admin.dto.request.AnnouncementSaveRequestDTO;
import com.hui.admin.dto.response.AnnouncementListDTO;
import com.hui.admin.dto.response.AnnouncementPageResponseDTO;
import com.hui.admin.dto.response.AnnouncementResponseDTO;
import com.hui.admin.entity.AnnouncementDO;
import com.hui.admin.mapper.AnnouncementMapper;
import com.hui.admin.service.AnnouncementService;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 公告服务实现
 */
@Service
public class AnnouncementServiceImpl extends ServiceImpl<AnnouncementMapper, AnnouncementDO> implements AnnouncementService {

    @Override
    public void createAnnouncement(AnnouncementSaveRequestDTO requestDTO) {
        LambdaQueryWrapper<AnnouncementDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AnnouncementDO::getTitle, requestDTO.getTitle());
        AnnouncementDO announcementDO = getOne(queryWrapper);
        if (announcementDO != null) {
            throw new AnnouncementException(ErrorCodeEnum.ANNOUNCEMENT_EXISTS);
        }
        AnnouncementDO result = new AnnouncementDO();
        try {
            BeanUtils.copyProperties(requestDTO, result);
        } catch (Exception e) {
            throw new AnnouncementException(ErrorCodeEnum.ANNOUNCEMENT_ADD_FAILED);
        }
        boolean isSaved = save(result);
        if (!isSaved) {
            throw new AnnouncementException(ErrorCodeEnum.ANNOUNCEMENT_ADD_FAILED);
        }
    }

    @Override
    public void deleteAnnouncement(Long id) {
        boolean isDeleted = removeById(id);
        if (!isDeleted) {
            throw new AnnouncementException(ErrorCodeEnum.ANNOUNCEMENT_NOT_EXISTS);
        }
    }

    @Override
    public void deleteAnnouncements(AnnouncementDeleteBatchRequestDTO requestDTO) {
        if ((ArrayUtil.isEmpty(requestDTO.getIds()))) {
            throw new UserException(ErrorCodeEnum.INVALID_PARAMS);
        }

        boolean flag = removeByIds(Arrays.asList(requestDTO.getIds()));

        if (!flag) {
            throw new UserException(ErrorCodeEnum.ANNOUNCEMENT_NOT_EXISTS);
        }
    }

    @Override
    public AnnouncementPageResponseDTO getAnnouncementPage(AnnouncementPageRequestDTO requestDTO) {
        // 创建分页对象
        Page<AnnouncementDO> page = new Page<>(requestDTO.getPageNum(), requestDTO.getPageSize());

        // 构建查询条件
        LambdaQueryWrapper<AnnouncementDO> queryWrapper = new LambdaQueryWrapper<>();

        if (requestDTO.getTitle() != null && !requestDTO.getTitle().isEmpty()) {
            queryWrapper.like(AnnouncementDO::getTitle, requestDTO.getTitle());
        }

        if (requestDTO.getContent() != null && !requestDTO.getContent().isEmpty()) {
            queryWrapper.like(AnnouncementDO::getContent, requestDTO.getContent());
        }

        // 按发布时间降序排列
        queryWrapper.orderByDesc(AnnouncementDO::getPublishTime);

        // 执行分页查询
        Page<AnnouncementDO> resultPage = baseMapper.selectPage(page, queryWrapper);

        // 转换 DO 到 DTO
        List<AnnouncementListDTO> dtoList = resultPage.getRecords().stream().map(doObj -> {
            AnnouncementListDTO dto = new AnnouncementListDTO();
            dto.setId(doObj.getId());
            dto.setTitle(doObj.getTitle());
            dto.setContent(doObj.getContent());
            dto.setPublishTime(doObj.getPublishTime());
            dto.setIsTop(doObj.getIsTop());
            dto.setStatus(doObj.getStatus());
            return dto;
        }).toList();

        // 构造响应对象并返回
        AnnouncementPageResponseDTO responseDTO = new AnnouncementPageResponseDTO();
        responseDTO.setList(dtoList);
        responseDTO.setTotal(resultPage.getTotal());

        return responseDTO;
    }

    @Override
    public void updateAnnouncement(AnnouncementSaveRequestDTO requestDTO) {
        LambdaUpdateWrapper<AnnouncementDO> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(AnnouncementDO::getId, requestDTO.getId())
                .set(AnnouncementDO::getTitle, requestDTO.getTitle())
                .set(AnnouncementDO::getContent, requestDTO.getContent())
                .set(AnnouncementDO::getIsTop, requestDTO.getIsTop())
                .set(AnnouncementDO::getStatus, requestDTO.getStatus())
                .set(AnnouncementDO::getPublishTime, requestDTO.getPublishTime());

        boolean flag = update(updateWrapper);

        if (!flag) {
            throw new AnnouncementException(ErrorCodeEnum.ANNOUNCEMENT_NOT_EXISTS);
        }
    }

    @Override
    public void updateAnnouncementStatus(Long id, Integer status) {
        LambdaQueryWrapper<AnnouncementDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AnnouncementDO::getId, id);
        AnnouncementDO result = getOne(queryWrapper);
        result.setStatus(status);
        updateById(result);
    }

    @Override
    public void updateAnnouncementIsTop(Long id, Integer isTop) {
        LambdaQueryWrapper<AnnouncementDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AnnouncementDO::getId, id);
        AnnouncementDO result = getOne(queryWrapper);
        result.setIsTop(isTop);
        updateById(result);
    }

    @Override
    public void exportAnnouncements(HttpServletResponse response, AnnouncementExportRequestDTO requestDTO) {
        try {
            LambdaQueryWrapper<AnnouncementDO> queryWrapper = new LambdaQueryWrapper<>();

            if (requestDTO.getTitle() != null) {
                queryWrapper.like(AnnouncementDO::getTitle, requestDTO.getTitle());
            }

            if (requestDTO.getContent() != null) {
                queryWrapper.like(AnnouncementDO::getContent, requestDTO.getContent());
            }

            if (CollUtil.isNotEmpty(requestDTO.getIds())) {
                queryWrapper.in(AnnouncementDO::getId, requestDTO.getIds());
            }

            List<AnnouncementDO> list = list(queryWrapper);

            ExcelWriter writer = ExcelUtil.getWriter(true);

            // 设置标题别名
            writer.addHeaderAlias("title", "公告标题");
            writer.addHeaderAlias("content", "公告内容");
            writer.addHeaderAlias("publishTime", "发布时间");
            writer.addHeaderAlias("isTop", "置顶（0:否,1:是）");
            writer.addHeaderAlias("status", "状态（0:禁用,1:启用）");


            // 只导出设置过标题的列
            writer.setOnlyAlias(true);

            // 写入数据
            writer.write(list, true);

            // 设置响应头
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");

            try (ServletOutputStream out = response.getOutputStream()) {
                writer.flush(out, true);
            }

            writer.close();

        } catch (IOException e) {
            throw new RuntimeException("导出失败", e);
        }
    }

    @Transactional
    @Override
    public void importAnnouncements(MultipartFile file) {

        try (InputStream is = file.getInputStream();
             ExcelReader reader = ExcelUtil.getReader(is)) {

            reader.addHeaderAlias("公告标题", "title");
            reader.addHeaderAlias("公告内容", "content");
            reader.addHeaderAlias("发布时间", "publishTime");
            reader.addHeaderAlias("置顶（0:否,1:是）", "isTop");
            reader.addHeaderAlias("状态（0:禁用,1:启用）", "status");

            List<AnnouncementDO> list = reader.readAll(AnnouncementDO.class);

            // 检查标题的唯一性
            Set<String> titles = new HashSet<>();

            for (AnnouncementDO announcement : list) {
                if (titles.contains(announcement.getTitle())) {
                    throw new AnnouncementException(ErrorCodeEnum.ANNOUNCEMENT_EXISTS);
                }
                titles.add(announcement.getTitle());
            }

            // 检查与数据库中已存在的数据是否冲突
            List<String> existingTitles = baseMapper.selectList(new LambdaQueryWrapper<AnnouncementDO>()
                            .in(AnnouncementDO::getTitle, titles)
                            .select(AnnouncementDO::getTitle))
                    .stream()
                    .map(AnnouncementDO::getTitle)
                    .toList();

            if (!existingTitles.isEmpty()) {
                throw new AnnouncementException(ErrorCodeEnum.ANNOUNCEMENT_TITLE_EXISTS);
            }

            saveBatch(list);

        } catch (IOException e) {
            throw new RuntimeException("导入失败", e);
        }
    }

    @Override
    public List<AnnouncementResponseDTO> getAnnouncementList() {
        // 构建查询条件：只查启用状态的公告
        LambdaQueryWrapper<AnnouncementDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(AnnouncementDO::getStatus, 0)
                .orderByDesc(AnnouncementDO::getPublishTime)
                .last("LIMIT 3");

        // 查询数据
        List<AnnouncementDO> announcementList = baseMapper.selectList(queryWrapper);

        return announcementList.stream()
                .map(announcement -> {
                    AnnouncementResponseDTO dto = new AnnouncementResponseDTO();
                    dto.setTitle(announcement.getTitle());
                    dto.setContent(announcement.getContent());
                    if (announcement.getPublishTime() != null) {
                        dto.setDate(announcement.getPublishTime().format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                    }
                    return dto;
                })
                .toList();
    }
}
